package top.saplf.sileny.net.netease

import android.util.Base64
import com.alibaba.fastjson.JSON
import java.math.BigInteger
import java.security.MessageDigest
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

/**
 * @author saplf
 */
private val random: Random by lazy { Random() }

private const val modulus = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
private const val nonce = "0CoJUm6Qyw8W8jud"
private const val pubKey = "010001"
private const val iv = "0102030405060708"

private val aesCipher: Cipher by lazy { Cipher.getInstance("AES/CBC/NoPadding") }
private val md5Cipher: MessageDigest by lazy { MessageDigest.getInstance("MD5") }

/**
 * 创建随机字符串，用作对称加密密钥
 * @param size 加密密钥的长度
 */
fun createSecretKey(size: Int) =
        ByteArray(size).apply { random.nextBytes(this) }.map(::byteToHex).joinToString("")

private fun byteToHex(byte: Byte): String {
    val tmp = byte.toInt()
    val low = tmp and 0xF
    val resultLow = if (low < 10) (low + 48).toChar() else (low + 87).toChar()
    val high = (tmp shr 4) and 0xF
    val resultHigh = if (high < 10) (high + 48).toChar() else (high + 87).toChar()
    return "$resultHigh$resultLow"
}

/**
 * AES 对称加密，padding 方法是补充模量
 * @param content 加密内容
 * @param key 加密密钥
 */
fun aesEncrypt(content: String, key: String): String {
    val blockSize = aesCipher.blockSize

    // 对齐数据
    val dataBytes: ByteArray
    val contentLength = content.length
    val pad = contentLength % blockSize
    if (pad != 0) {
        val size = contentLength + blockSize - pad
        dataBytes = ByteArray(size)
        System.arraycopy(content.toByteArray(), 0, dataBytes, 0, contentLength)
        ((size - 1) downTo contentLength).forEach { dataBytes[it] = pad.toByte() }
    } else {
        dataBytes = content.toByteArray()
    }

    val keySpec = SecretKeySpec(key.toByteArray(), "AES")
    val ivSpec = IvParameterSpec(iv.toByteArray())
    aesCipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
    return Base64.encodeToString(aesCipher.doFinal(dataBytes), Base64.NO_WRAP)
}

fun rsaEncrypt(content: String): String {
    val reversed = content.reversed()
    val rs = BigInteger(reversed, 16).modPow(BigInteger(pubKey, 16), BigInteger(modulus, 16))
    return rs.toString(16).zfill(256)
}

/**
 * 补充字符串至指定长度
 */
private fun String.zfill(size: Int, fill: String = "0"): String {
    if (length >= size) {
        return this
    }
    val builder = StringBuilder()
    (0 until length).forEach { builder.append(fill) }
    return builder.toString() + this
}

fun encryptedRequest(any: Any): Map<String, String> {
    val text = JSON.toJSONString(any)
    val secret = createSecretKey(16)
    val encText = aesEncrypt(aesEncrypt(text, nonce), secret)
    val encSecret = rsaEncrypt(secret)
    return mapOf(
            "params" to encText,
            "encSecKey" to encSecret
    )
}

